﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class LevelColliderMaker : MonoBehaviour, IPlayLevelElements
{
	class Cluster: IPlayLevelElements //<- this is a horizontal cluster test
	{
		List<CellGrid> cellSet;
		GameObject clusterCollider;
		
		int yGridPos;
		int layerMask;
		
		//Pass the initial cell determinant
		public Cluster(CellGrid cellGrid)
		{
			cellSet = new List<CellGrid>();
			cellSet.Add(cellGrid);
			
			//Row target 
			yGridPos = cellGrid.gridPosY;
			
			//Add new GameObject for the collider
			GameObject go = new GameObject("ClusterCollider");
			go.AddComponent<BoxCollider>();
			go.transform.position = cellGrid.transform.position;
			
			//Prevent cluster collider from receiving raycast
			go.layer = LayerMask.NameToLayer("Ignore Raycast");
			layerMask = cellGrid.gameObject.layer;
			
			//Assign the collider
			clusterCollider = go;
		}
		
		void addCell(CellGrid cellGrid)
		{
			cellSet.Add(cellGrid);
			
			//Get the average position of the cellSet
			float xAverage = 0;
			foreach(CellGrid cg in cellSet)
				xAverage += cg.transform.localPosition.x;
			
			Vector3 position = clusterCollider.transform.localPosition;
			position.x = xAverage/ cellSet.Count;
			
			//Scale the collider base on the number of cell
			clusterCollider.transform.localPosition = position;
			Vector3 scale = clusterCollider.transform.localScale;
			scale.x = 1 * cellSet.Count;
			clusterCollider.transform.localScale = scale;
		}
		
		public int MaskLayer
		{
			get{return layerMask;}
		}
		
		public int GridPosY
		{
			get{return yGridPos;}
		}
		
		//Check if this cell is appropriate with these cluster
		//Add the cell as part of the cluster
		public bool testCell(CellGrid cellGrid)
		{
			//Cell grid is not appropriate for its yGrid position
			if(cellGrid.gridPosY != yGridPos)
				return false;
			
			//Cell grid is not within the Z - Position of the Cluster
			if(cellGrid.gameObject.layer !=  layerMask)
				return false;
			
			//Cluster already contains this grid
			if (cellSet.Contains(cellGrid))
				return true;
			
			foreach(CellGrid cg in cellSet)
			{
				if(cg.gridPosX + 1 == cellGrid.gridPosX)
				{
					addCell(cellGrid);
					return true;
				}
				
				if(cg.gridPosX - 1 == cellGrid.gridPosX)
				{
					addCell(cellGrid);
					return true;
				}
			}
			return false;
		}
		
		public void Strip_Add_Components ()
		{
			GameObject go = MonoBehaviour.Instantiate(LevelAssetManager.Instance.getAsset<GameObject>(KEYTYPE.PREFAB, "GroundTile")) as GameObject;
			go.transform.position = clusterCollider.transform.position;
			go.transform.localScale = clusterCollider.transform.localScale;
			MonoBehaviour.Destroy(clusterCollider);
			clusterCollider = go;
			clusterCollider.collider.isTrigger =  true;
			clusterCollider.tag = "Ground";
		}
		public List<CellGrid> getCluster()
		{
			return cellSet;
		}
		public void Destroy()
		{
			MonoBehaviour.Destroy(clusterCollider);	
		}
		
		public void setZPosition(float zPos)
		{
			Vector3 pos = clusterCollider.transform.position;
			pos.z = zPos;
			pos.y += 2;
			clusterCollider.transform.position = pos;
		}
	}
	
	//List of all collider cluster generated
	List<Cluster> clusters;
	
	static LevelColliderMaker instance;
	readonly static string[] ignoreTexture = {"F1A", "F1B", "F1C", "F2A", "F2B", "F2C", "F3A", "F3B"};
	
	void Awake()
	{
		instance = this;
	}
	
	// Use this for initialization
	void Start () 
	{
		clusters = new List<Cluster>();
	}
	
	
	public void Load(XMLData.Map.XMLCluster[] clusterSet, bool isLevelBuilding)
	{
		clusters = new List<Cluster>();
		foreach(XMLData.Map.XMLCluster _cluster in clusterSet)
		{
			LevelGrid lg = LevelGrid.Instance;
			GameObject[,] world = (_cluster.layerMask == LayerMask.NameToLayer("Player1Specific"))? lg.World1:lg.World2;
			foreach(XMLData.Map.TilePos tilePos in _cluster.tileUnit)
			{
				CellGrid cg = world[tilePos.gridPosX,tilePos.gridPosY].GetComponent<CellGrid>();
				addCell(cg);
			}
		}
		if(!isLevelBuilding)
			LevelColliderMaker.Instance.Strip_Add_Components();
	}
	
	public XMLData.Map.XMLCluster[] Package()
	{
		List<XMLData.Map.XMLCluster> xmlClusterList = new List<XMLData.Map.XMLCluster>();
		
		foreach(Cluster c in clusters)
		{
			XMLData.Map.XMLCluster xmlCluster = new XMLData.Map.XMLCluster();
			xmlCluster.gridPosY = c.GridPosY;
			xmlCluster.layerMask = c.MaskLayer;
			
			List<XMLData.Map.TilePos> tileUnits = new List<XMLData.Map.TilePos>();
			foreach(CellGrid cg in c.getCluster())
			{
				XMLData.Map.TilePos tp = new XMLData.Map.TilePos();
				tp.gridPosX = cg.gridPosX;
				tp.gridPosY = cg.gridPosY;
				tileUnits.Add(tp);
			}
			xmlCluster.tileUnit = tileUnits.ToArray();
			xmlClusterList.Add(xmlCluster);
		}
		return xmlClusterList.ToArray();
	}
	
	public void DestroyClusters()
	{
		foreach(Cluster cluster in clusters)
		{
			cluster.Destroy();
		}
	}
	
	public bool isIgnored(string name)
	{
		string _name = name.ToUpper();
		foreach(string ignore in ignoreTexture)
		{
			if(_name.Contains(ignore))
				return true;
		}
		return false;
	}
	public void addCell(CellGrid cellGrid)
	{
		if(cellGrid.renderer.material.mainTexture == null)
			return;
		
		if(isIgnored(cellGrid.renderer.material.mainTexture.name))
			return;
		
		//Check if this is the first cluster
		if(clusters.Count == 0)
		{
			//Create the first cluster
			Cluster cluster = new Cluster(cellGrid);
			clusters.Add(cluster);
			return;
		}
		
		//Check if this is grid belongs to any cluster 
		int clusterCount = 0;
		foreach(Cluster _cluster in clusters)
		{
			if(_cluster.testCell(cellGrid))
			{
				//Try merging this cluster is possible
				mergeCluster(_cluster, clusterCount);
				return;
			}
			clusterCount++;
		}
		
		//Cell grid belong to a new cluster
		Cluster newCluster = new Cluster(cellGrid);
		clusters.Add(newCluster);
	}

	public void deleteCell(CellGrid cellGrid)
	{
		//Check if this is grid belongs to any cluster 
		int clusterCount = 0;
		foreach(Cluster _cluster in clusters)
		{
			if(_cluster.testCell(cellGrid))
			{
				splitCluster(_cluster, cellGrid);
				return;
			}
			clusterCount++;
		}
	}
	
	void splitCluster(Cluster cluster, CellGrid targetCell)
	{
		//Debug.Log(clusters.Count);
		cluster.getCluster().Remove(targetCell);
		CellGrid[] cellGridArray = (CellGrid[])cluster.getCluster().ToArray().Clone();
		cluster.Destroy();
		clusters.Remove(cluster);
		cluster = null;
		
		foreach(CellGrid cg in cellGridArray)
		{
			this.addCell(cg);
		}
		
	}
	
	void mergeCluster(Cluster newCluster, int clusterIndex)
	{
		for(int i = clusters.Count -1; i >= 0; i--)
		{
			//Continue to other cluster when this cluster is just checking himself
			if(i == clusterIndex) continue;
			
			Cluster currentCluster = clusters[i];
			//Continue to other cluster when this cluster is not within the row
			if(currentCluster.GridPosY != newCluster.GridPosY) continue;
			
			//Cell grid is not within the Z - Position of the Cluster
			if(currentCluster.MaskLayer != newCluster.MaskLayer) continue;
			
			//Get cluster set of the new created cluster
			List<CellGrid> cellSet = newCluster.getCluster();
			
			//Check if this newCluster
			for(int j = 0; j < cellSet.Count; j++)
			{
				if(currentCluster.testCell(cellSet[j]))
				{
					while(cellSet.Count > 0)
					{
						for(int k = 0; k < cellSet.Count; k++)
						{
							if(currentCluster.testCell(cellSet[k]))
							{
								cellSet.RemoveAt(k);
								continue;
							}
						}
					}
					newCluster.Destroy();
					clusters.RemoveAt(clusterIndex);
					return;
				}
			}
		}
	}
	
	public void Strip_Add_Components ()
	{
		Cluster[] clusterArray = clusters.ToArray();
		for(int i = 0 ; i < clusterArray.Length; i++)
		{
			Cluster cluster = clusterArray[i];
			
			if(cluster.GridPosY == 0)
			{
				cluster.Destroy();
				try
				{
					clusters.RemoveAt(i);
				}catch{}
				continue;
			}
			cluster.Strip_Add_Components();
			
			if(cluster.MaskLayer == LayerMask.NameToLayer("Player1Specific"))
			{
				cluster.setZPosition(3);
			}
			
			else if(cluster.MaskLayer == LayerMask.NameToLayer("Player2Specific"))
			{
				cluster.setZPosition(0);
			}
		}
	}
	public static LevelColliderMaker Instance
	{
		get{return instance;}
	}
}
